#!/usr/bin/evn python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   文件名称 :     app
   文件功能描述 :   功能描述
   创建人 :       小钟同学
   创建时间 :          2021/7/15
-------------------------------------------------
   修改描述-2021/7/15:         
-------------------------------------------------
"""

from apps.config.docs_conf import docs
from fastapi.middleware.cors import CORSMiddleware
from fastapi import Cookie, Depends, FastAPI, Query, WebSocket, status, Request


class FastSkeletonApp:
    # 定义FastAPIapp实例的对象
    startge = FastAPI(
        title=docs.TITLE,
        description=docs.DESC,
        version="V1.0.0",
        # debug 是否再返回结果里面显示错误异常信息
        debug=False,
        docs_url=docs.DOCS_URL,
        openapi_url=docs.OPENAPI_URL,
        redoc_url=docs.REDOC_URL,
        openapi_tags=docs.TAGS_METADATA,
        servers=docs.SERVERS
    )

    def __init__(self):
        pass

        self.register_global_logger()  # 注册日志处理记录初始化信息
        self.register_global_exception()  # 注册全局异常捕获信息
        self.register_global_cors()  # 全局配置跨域设置
        self.register_global_middleware()  # 注册全局中间件的注册
        self.register_global_event()  # 注册全局的启动和关闭事件
        self.register_global_ext_plugs()  # 注册所有自定义的或者第三的扩展插件

        # =====================PS=====================
        # 如果需要开启日志记录--优先级最高！必须在如有开始注册之前进行注册
        # =====================PS=====================
        self.register_global_app_contexr_logger_route()  # app注册的路由也加上日志记录
        # self.register_global_include_router()  # 批量导入注册路由
        self.register_global_health_check()  # 默认注册开启健康检测的URL检测

        self.register_global_websocket_router()  # 注册websocketAPI
        self.register_global_include_router()  # 批量导入注册路由

        # PS:如果想看到打印的所有的地址就需要把注册两次启动的是才看到！！！如果不行看可以不用注册两侧，路由依然注册生效
        self.register_global_include_router()  # 批量导入各个模块的下所有的路由信息
        # self.register_global_include_router()  # 批量导入各个模块的下所有的路由信息

    def register_global_logger(self):
        '''
        处理日志的【】配置初始化操作
        :param app:
        :return:
        '''
        # 引入函数和对应的日对象-在当前的APPS目录下建立日志收集管理目录
        import os
        from apps.ext.logger import creat_customize_log_loguru, logger
        creat_customize_log_loguru(pro_path=os.path.split(os.path.realpath(__file__))[0])

    def register_global_ext_plugs(self):
        pass
        # # 初始化第三的请求HTTP客户端对象
        from apps.ext.asynhttp import async_client
        async_client.init_app(self.startge)
        #
        # 初始化redis客户端
        from apps.ext.pooled_postgresql import sync_pooled_postgresql_client
        sync_pooled_postgresql_client.init_app(self.startge)

        # 初始化同步redis客户端对象
        from apps.ext.redis.syncredis2 import sync_redis_client
        sync_redis_client.init_app(self.startge)

        # 创建队列
        # sync_rabbit_client.creat_dead_exchange_and_queue()

    def register_global_event(self):
        pass
        # 注册App结束的时候，清楚相关的异步任务

    def register_include_router(self):
        '''
        导入路由模块
        :param app:
        :return:
        '''
        pass

    def register_global_middleware(self):
        '''
        配置中间件
         # 中间件执行的顺序是，谁先注册,谁就再最内层，它的再后一个注册，就再最外层
        :param app:
        :return:
        '''
        pass
        from apps.middleware.global_auth import AuthMiddleware
        # self.startge.add_middleware(AuthMiddleware)
        from apps.middleware.global_requet import GlobalQuestyMiddleware
        self.startge.add_middleware(GlobalQuestyMiddleware)

        # 如果是也要记录用户提交的参数信息的是错误的情况也好分析的，把日志放最外层
        # self.app.add_middleware(LoggingMiddleware)
        # from apps.middleware.global_errer import GlobalErrorMiddleware
        # self.startge.add_middleware(GlobalErrorMiddleware)

    def register_global_cors(self):
        '''
        处理全局的跨域
        :param app:
        :return:
        '''
        pass
        self.startge.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

    def register_global_exception(self):
        '''
        配置我们的所有的异常
        :param app:
        :return:
        '''
        pass
        from apps.ext.exception import ApiExceptionHandler
        ApiExceptionHandler().init_app(self.startge)

    def register_global_app_contexr_logger_route(self):
        '''
        是否全局的配注所有的A使用APP直接注册的方式注册的路由进行日志记录
        :return:
        '''
        pass
        # 要开启日志的激励的话，这个地方需要执行初始化化追踪的ID
        from apps.ext.logger import ContextLogerRoute
        self.startge.router.route_class = ContextLogerRoute

    def register_global_health_check(self):
        pass
        app = self.startge

        @app.get('/check', tags=['默认开启的健康检查'])
        async def health_check(re: Request):
            from apps.ext.logger import ContextLogerRoute, logger
            ContextLogerRoute.sync_trace_add_log_record_norequest(event_type='预扣库存信息', msg='你也打好的哈')
            # ContextLogerRoute.sync_trace_add_log_record(re,event_type='预扣库存信息222', msg='你也打好的哈')

            return 'ok242342'

    def register_global_include_router(self):
        '''
        导入路由模块
        :param app:
        :return:
        '''
        pass
        from apps.modules import init_routes
        init_routes(self.startge)

    def mount_static_files(self):
        # 装载静态目录
        from starlette.staticfiles import StaticFiles
        import os
        # 项目根目录
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        # 静态文件目录
        STATIC_DIR = os.path.join(BASE_DIR, 'static')
        self.startge.mount("/static", StaticFiles(directory=STATIC_DIR))

    def register_global_websocket_router(self):
        app = self.startge

        @app.websocket("/nw/websocket/{userid}")
        async def websocket_userid(websocket: WebSocket, userid: str):
            # 等待连接
            await websocket.accept()
            # 处理链接
            while True:
                # 接收发送过来的数据信息
                data = await websocket.receive_text()
                # 把接收过来的数据再一次的发送回去
                await websocket.send_text(f"ok")
                # 如果存在参数信息
